home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_ghostscript.idb / usr / freeware / lib / ghostscript / 3.33 / bdftops.ps.z / bdftops.ps
Encoding:
Text File  |  1998-05-21  |  22.5 KB  |  788 lines

  1. %    Copyright (C) 1990, 1995 Aladdin Enterprises.  All rights reserved.
  2. % This file is part of GNU Ghostscript.
  3. % GNU Ghostscript is distributed in the hope that it will be useful, but
  4. % WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility to
  5. % anyone for the consequences of using it or for whether it serves any
  6. % particular purpose or works at all, unless he says so in writing.  Refer
  7. % to the GNU Ghostscript General Public License for full details.
  8.  
  9. % bdftops.ps
  10. % Convert a BDF file (possibly with (an) associated AFM file(s))
  11. % to a PostScript Type 1 font (without eexec encryption).
  12. % The resulting font will work with any PostScript language interpreter,
  13. % but not with ATM or other font rasterizers lacking a complete interpreter.
  14.  
  15. /envBDF 120 dict def
  16. envBDF begin
  17.  
  18. % "Import" the image-to-path package.
  19. % This also brings in the Type 1 opcodes (type1ops.ps).
  20.    (impath.ps) run
  21.  
  22. % "Import" the font-writing package.
  23.    (wrfont.ps) run
  24.    wrfont_dict begin
  25.      /binary_CharStrings false def
  26.      /binary_tokens false def
  27.      /encrypt_CharStrings true def
  28.      /standard_only true def
  29.    end
  30.    /lenIV 0 def
  31.  
  32. % Invert the StandardEncoding vector.
  33.    256 dict dup begin
  34.    0 1 255 { dup StandardEncoding exch get exch def } for
  35.    end /StandardDecoding exch def
  36.  
  37. % Define the properties copied to FontInfo.
  38.    mark
  39.      (COPYRIGHT) /Notice
  40.      (FAMILY_NAME) /FamilyName
  41.      (FULL_NAME) /FullName
  42.      (WEIGHT_NAME) /Weight
  43.    .dicttomark /properties exch def
  44.  
  45. % Define the character sequences for synthesizing missing composite
  46. % characters in the standard encoding.
  47.    mark
  48.      /AE [/A /E]
  49.      /OE [/O /E]
  50.      /ae [/a /e]
  51.      /ellipsis [/period /period /period]
  52.      /emdash [/hyphen /hyphen /hyphen]
  53.      /endash [/hyphen /hyphen]
  54.      /fi [/f /i]
  55.      /fl [/f /l]
  56.      /germandbls [/s /s]
  57.      /guillemotleft [/less /less]
  58.      /guillemotright [/greater /greater]
  59.      /oe [/o /e]
  60.      /quotedblbase [/comma /comma]
  61.    .dicttomark /composites exch def
  62.  
  63. % Define the procedure for synthesizing composites.
  64. % This must not be bound.
  65.    /compose
  66.     { exch pop
  67.       FontMatrix Private /composematrix get invertmatrix concat
  68.       0 0 moveto
  69.       dup gsave false charpath pathbbox currentpoint grestore
  70.       6 2 roll setcachedevice show
  71.     } def
  72. % Define the CharString procedure that calls compose, with the string
  73. % on the stack.  This too must remain unbound.
  74.    /compose_proc
  75.     { Private /compose get exec
  76.     } def
  77.  
  78. % Define aliases for missing characters similarly.
  79.    mark
  80.      /acute /quoteright
  81.      /bullet /asterisk
  82.      /cedilla /comma
  83.      /circumflex /asciicircum
  84.      /dieresis /quotedbl
  85.      /dotlessi /i
  86.      /exclamdown /exclam
  87.      /florin /f
  88.      /fraction /slash
  89.      /grave /quoteleft
  90.      /guilsinglleft /less
  91.      /guilsinglright /greater
  92.      /hungarumlaut /quotedbl
  93.      /periodcentered /asterisk
  94.      /questiondown /question
  95.      /quotedblleft /quotedbl
  96.      /quotedblright /quotedbl
  97.      /quotesinglbase /comma
  98.      /quotesingle /quoteright
  99.      /tilde /asciitilde
  100.    .dicttomark /aliases exch def
  101.  
  102. % Define overstruck characters that can be synthesized with seac.
  103.    mark
  104.     [ /Aacute /Acircumflex /Adieresis /Agrave /Aring /Atilde
  105.       /Ccedilla
  106.       /Eacute /Ecircumflex /Edieresis /Egrave
  107.       /Iacute /Icircumflex /Idieresis /Igrave
  108.       /Lslash
  109.       /Ntilde
  110.       /Oacute /Ocircumflex /Odieresis /Ograve /Otilde
  111.       /Scaron
  112.       /Uacute /Ucircumflex /Udieresis /Ugrave
  113.       /Yacute /Ydieresis
  114.       /Zcaron
  115.       /aacute /acircumflex /adieresis /agrave /aring /atilde
  116.       /ccedilla
  117.       /eacute /ecircumflex /edieresis /egrave
  118.       /iacute /icircumflex /idieresis /igrave
  119.       /lslash
  120.       /ntilde
  121.       /oacute /ocircumflex /odieresis /ograve /otilde
  122.       /scaron
  123.       /uacute /ucircumflex /udieresis /ugrave
  124.       /yacute /ydieresis
  125.       /zcaron
  126.     ]
  127.     { dup =string cvs
  128.       [ exch dup 0 1 getinterval cvn
  129.     exch dup length 1 sub 1 exch getinterval cvn
  130.       ]
  131.     } forall
  132.      /cent [/c /slash]
  133.      /daggerdbl [/bar /equal]
  134.      /divide [/colon /hyphen]
  135.      /sterling [/L /hyphen]
  136.      /yen [/Y /equal]
  137.    .dicttomark /accentedchars exch def
  138.  
  139. % ------ Output utilities ------ %
  140.  
  141.    /ws {psfile exch writestring} bind def
  142.    /wl {ws (\n) ws} bind def
  143.    /wt {=string cvs ws ( ) ws} bind def
  144.  
  145. % ------ BDF file parsing utilities ------ %
  146.  
  147. % Define a buffer for reading the BDF file.
  148.    /buffer 400 string def
  149.  
  150. % Read a line from the BDF file into the buffer.
  151. % Ignore empty (zero-length) lines.
  152. % Define /keyword as the first word on the line.
  153. % Define /args as the remainder of the line.
  154. % If the keyword is equal to commentword, skip the line.
  155. % (If commentword is equal to a space, never skip.)
  156.    /nextline
  157.     {  { bdfile buffer readline not
  158.       { (Premature EOF\n) print stop } if
  159.      dup length 0 ne { exit } if pop     
  160.        }
  161.       loop
  162.       ( ) search
  163.        { /keyword exch def pop }
  164.        { /keyword exch def () }
  165.       ifelse
  166.       /args exch def
  167.       keyword commentword eq { nextline } if
  168.     } bind def
  169.  
  170. % Get a word argument from args.  We do *not* copy the string.
  171.    /warg        % warg -> string
  172.     { args ( ) search
  173.        { exch pop exch }
  174.        { () }
  175.       ifelse  /args exch def
  176.     } bind def
  177.  
  178. % Get an integer argument from args.
  179.    /iarg        % iarg -> int
  180.     { warg cvi
  181.     } bind def
  182.  
  183. % Get a numeric argument from args.
  184.    /narg        % narg -> int|real
  185.     { warg cvr
  186.       dup dup cvi eq { cvi } if
  187.     } bind def
  188.  
  189. % Convert the remainder of args into a string.
  190.    /remarg        % remarg -> string
  191.     { args copystring
  192.     } bind def
  193.  
  194. % Get a string argument that occupies the remainder of args.
  195.    /sarg        % sarg -> string
  196.     { args (") anchorsearch
  197.        { pop /args exch def } { pop } ifelse
  198.       args args length 1 sub get (") 0 get eq
  199.        { args 0 args length 1 sub getinterval /args exch def } if
  200.       args copystring
  201.     } bind def
  202.  
  203. % Check that the keyword is the expected one.
  204.    /checkline        % (EXPECTED-KEYWORD) checkline ->
  205.     { dup keyword ne
  206.        { (Expected ) print =
  207.          (Line=) print keyword print ( ) print args print (\n) print stop
  208.        } if
  209.       pop
  210.     } bind def
  211.  
  212. % Read a line and check its keyword.
  213.    /getline        % (EXPECTED-KEYWORD) getline ->
  214.     { nextline checkline
  215.     } bind def
  216.  
  217. % Find the first/last non-zero bit of a non-zero byte.
  218.    /fnzb
  219.     { 0 { exch dup 128 ge { pop exit } { dup add exch 1 add } ifelse }
  220.       loop
  221.     } bind def
  222.    /lnzb
  223.     { 7 { exch dup 1 and 0 ne { pop exit } { -1 bitshift exch 1 sub } ifelse }
  224.       loop
  225.     } bind def
  226.  
  227. % ------ Type 1 encoding utilities ------ %
  228.  
  229. % Parse the side bearing and width information that begins a CharString.
  230. % Arguments: charstring.  Result: sbx sby wx wy substring.
  231.    /parsesbw
  232.     { mark exch lenIV
  233.        {        % stack: mark ... string dropcount
  234.          dup 2 index length exch sub getinterval
  235.      dup 0 get dup 32 lt { pop exit } if
  236.      dup 246 le
  237.       { 139 sub exch 1 }
  238.       { dup 250 le
  239.          { 247 sub 8 bitshift 108 add 1 index 1 get add exch 2 }
  240.          { dup 254 le
  241.         { 251 sub 8 bitshift 108 add 1 index 1 get add neg exch 2 }
  242.         { pop dup 1 get 128 xor 128 sub
  243.           8 bitshift 1 index 2 get add
  244.           8 bitshift 1 index 3 get add
  245.           8 bitshift 1 index 4 get add exch 5
  246.         } ifelse
  247.          } ifelse
  248.       } ifelse
  249.        } loop
  250.       counttomark 3 eq { 0 3 1 roll 0 exch } if
  251.       6 -1 roll pop
  252.     } bind def 
  253.  
  254. % Find the side bearing and width information that begins a CharString.
  255. % Arguments: charstring.  Result: charstring sizethroughsbw.
  256.    /findsbw
  257.     { dup parsesbw 4 { exch pop } repeat skipsbw
  258.     } bind def
  259.    /skipsbw        % charstring sbwprefix -> sizethroughsbw
  260.     { length 1 index length exch sub
  261.       2 copy get 12 eq { 2 } { 1 } ifelse add
  262.     } bind def
  263.  
  264. % Encode a number, and append it to a string.
  265. % Arguments: str num.  Result: newstr.
  266.    /concatnum
  267.     { dup dup -107 ge exch 107 le and
  268.        { 139 add 1 string dup 0 3 index put }
  269.        { dup dup -1131 ge exch 1131 le and
  270.           { dup 0 ge { 16#f694 } { neg 16#fa94 } ifelse add
  271.         2 string dup 0 3 index -8 bitshift put
  272.         dup 1 3 index 255 and put
  273.       }
  274.       { 5 string dup 0 255 put exch
  275.         2 copy 1 exch -24 bitshift 255 and put
  276.         2 copy 2 exch -16 bitshift 255 and put
  277.         2 copy 3 exch -8 bitshift 255 and put
  278.         2 copy 4 exch 255 and put
  279.         exch
  280.       }
  281.      ifelse
  282.        }
  283.       ifelse exch pop concatstrings
  284.     } bind def
  285.  
  286. % ------ Point arithmetic utilities ------ %
  287.  
  288.    /ptadd { exch 4 -1 roll add 3 1 roll add } bind def
  289.    /ptexch { 4 2 roll } bind def
  290.    /ptneg { neg exch neg exch } bind def
  291.    /ptpop { pop pop } bind def
  292.    /ptsub { ptneg ptadd } bind def
  293.  
  294. % ------ The main program ------ %
  295.  
  296.    /readBDF        % <infilename> <outfilename> <fontname>
  297.             %   <encodingname> <uniqueID> <xuid> readBDF -> <font>
  298.     { /xuid exch def        % may be null
  299.       /uniqueID exch def    % may be -1
  300.       /encodingname exch def
  301.     /encoding encodingname cvx exec def
  302.       /fontname exch def
  303.       /psname exch def
  304.       /bdfname exch def
  305.       gsave        % so we can set the CTM to the font matrix
  306.  
  307. %  Open the input files.  We don't open the output file until
  308. %  we've done a minimal validity check on the input.
  309.       bdfname (r) file /bdfile exch def
  310.       /commentword ( ) def
  311.  
  312. %  Check for the STARTFONT.
  313.       (STARTFONT) getline
  314.       args (2.1) ne { (Not version 2.1\n) print stop } if
  315.  
  316. %  Initialize the font.
  317.       /Font 20 dict def
  318.       Font begin
  319.       /FontName fontname def
  320.       /PaintType 0 def
  321.       /FontType 1 def
  322.       uniqueID 0 gt { /UniqueID uniqueID def } if
  323.       xuid null ne { /XUID xuid def } if
  324.       /Encoding encoding def
  325.       /FontInfo 20 dict def
  326.       /Private 20 dict def
  327.       currentdict end currentdict end
  328.       exch begin begin        % insert font above environment
  329.  
  330. %  Initialize the Private dictionary in the font.
  331.       Private begin
  332.       /-! {string currentfile exch readhexstring pop} readonly def
  333.       /-| {string currentfile exch readstring pop} readonly def
  334.       /|- {readonly def} readonly def
  335.       /| {readonly put} readonly def
  336.       /BlueValues [] def
  337.       /lenIV lenIV def
  338.       /MinFeature {16 16} def
  339.       /password 5839 def
  340.       /UniqueID uniqueID def
  341.       end        % Private
  342.  
  343. %  Invert the Encoding, for synthesizing composite characters.
  344.       /decoding encoding length dict def
  345.       0 1 encoding length 1 sub
  346.        { dup encoding exch get exch decoding 3 1 roll put }
  347.       for
  348.  
  349. %  Now open the output file.
  350.       psname (w) file /psfile exch def
  351.  
  352. %  Put out a header compatible with the Adobe "standard".
  353.       (%!FontType1-1.0: ) ws fontname wt (000.000) wl
  354.       (% This is a font description converted from ) ws
  355.     bdfname wl
  356.       (%   by bdftops running on ) ws
  357.       statusdict /product get ws ( revision ) ws
  358.       revision =string cvs ws (.) wl
  359.  
  360. %  Copy the initial comments, up to FONT.
  361.       true
  362.        { nextline
  363.      keyword (COMMENT) ne {exit} if
  364.       { (% Here are the initial comments from the BDF file:\n%) wl
  365.       } if false
  366.      (%) ws remarg wl
  367.        } loop pop
  368.       () wl
  369.       /commentword (COMMENT) def    % do skip comments from now on
  370.  
  371. %  Read and process the FONT, SIZE, and FONTBOUNDINGBOX.
  372.       % If we cared about FONT, we'd use it here.  If the BDF files
  373.       % from MIT had PostScript names rather than X names, we would
  374.       % care; but what's there is unusable, so we discard FONT.
  375.       % The FONTBOUNDINGBOX may not be reliable, so we discard it too.
  376.       (FONT) checkline
  377.       (SIZE) getline
  378.     /pointsize iarg def   /xres iarg def   /yres iarg def
  379.       (FONTBOUNDINGBOX) getline
  380.       nextline
  381.  
  382. %  Initialize the font bounding box bookeeping.
  383.       /fbbxo 1000 def
  384.       /fbbyo 1000 def
  385.       /fbbxe -1000 def
  386.       /fbbye -1000 def
  387.  
  388. %  Read and process the properties.  We only care about a few of them.
  389.       keyword (STARTPROPERTIES) eq
  390.        { iarg
  391.           { nextline
  392.         properties keyword known
  393.          { FontInfo properties keyword get sarg readonly put
  394.          } if
  395.       } repeat
  396.          (ENDPROPERTIES) getline
  397.      nextline
  398.        } if
  399.  
  400. %  Compute and set the FontMatrix.
  401.       Font /FontMatrix
  402.        [ 0.001 0 0 0.001 xres mul yres div 0 0 ] readonly
  403.       dup setmatrix put
  404.  
  405. %  Read and process the header for the bitmaps.
  406.       (CHARS) checkline
  407.     /ccount iarg def
  408.  
  409. %  Initialize the CharStrings dictionary.
  410.       /charstrings ccount
  411.     composites length add
  412.     aliases length add
  413.     accentedchars length add
  414.     1 add dict def        % 1 add for .notdef
  415.       /isfixedwidth true def
  416.       /fixedwidth null def
  417.       /subrcount 0 def
  418.       /subrs [] def
  419.  
  420. %  Read the bitmap data.  This reads the remainder of the file.
  421. %  We do this before processing the bitmaps so that we can compute
  422. %  the correct FontBBox first.
  423.       /chardata ccount dict def
  424.       ccount -1 1
  425.        { (STARTCHAR) getline
  426.            /charname remarg def
  427.      (ENCODING) getline
  428.        /eindex iarg def
  429.        eindex 0 ge
  430.         { charname /charname StandardEncoding eindex get def
  431.           charname /.notdef eq eindex 0 gt and
  432.            { /charname (A) eindex =string cvs concatstrings cvn def
  433.            }
  434.           if
  435.           (/) print charname =string cvs print (,) print print
  436.         }
  437.         { (/) print charname print
  438.         }
  439.        ifelse
  440.        10 mod 1 eq { (\n) print flush } if
  441.      (SWIDTH) getline
  442.        /swx iarg pointsize mul 1000 div xres mul 72 div def
  443.        /swy iarg pointsize mul 1000 div xres mul 72 div def
  444.      (DWIDTH) getline        % Ignore, use SWIDTH instead
  445.      (BBX) getline
  446.        /bbw iarg def  /bbh iarg def  /bbox iarg def  /bboy iarg def
  447.      nextline
  448.      keyword (ATTRIBUTES) eq
  449.       { nextline
  450.       } if
  451.      (BITMAP) checkline
  452.  
  453. % Update the font bounding box.
  454.      /fbbxo fbbxo bbox min def
  455.      /fbbyo fbbyo bboy min def
  456.      /fbbxe fbbxe bbox bbw add max def
  457.      /fbbye fbbye bboy bbh add max def
  458.  
  459. % Read the bits for this character.
  460.      /raster bbw 7 add 8 idiv def
  461.      /cbits raster bbh mul string def
  462.      0 raster cbits length raster sub
  463.       { cbits exch raster getinterval
  464.         bdfile buffer readline not
  465.          { (EOF in bitmap\n) print stop } if
  466.         % stack has <cbits.interval> <buffer.interval>
  467.         0 () /SubFileDecode filter
  468.         exch 2 copy readhexstring pop pop pop closefile
  469.       } for
  470.      (ENDCHAR) getline
  471.  
  472. % Save the character data.
  473.      chardata charname [swx swy bbw bbh bbox bboy cbits] put
  474.        } for
  475.  
  476.       (ENDFONT) getline
  477.  
  478. % Allocate the buffers for the bitmap and the outline,
  479. % according to the font bounding box.
  480.       /fbbw fbbxe fbbxo sub def
  481.       /fbbh fbbye fbbyo sub def
  482.       /fraster fbbw 7 add 8 idiv def
  483.       /bits fraster fbbh mul 200 max 65535 min string def
  484.       /outline bits length 6 mul 65535 min string def
  485.  
  486. %  Process the characters.
  487.       chardata
  488.        { exch /charname exch def  aload pop
  489.      /cbits exch def
  490.      /bboy exch def   /bbox exch def
  491.      /bbh exch def   /bbw exch def
  492.      /swy exch def   /swx exch def
  493.  
  494. % The bitmap handed to type1imagepath must have the correct height,
  495. % because type1imagepath uses this to compute the scale factor,
  496. % so we have to clear the unused parts of it.
  497.      /raster bbw 7 add 8 idiv def
  498.      bits dup 0 1 raster fbbh mul 1 sub
  499.       { 0 put dup } for
  500.      pop pop
  501.      bits raster fbbh bbh sub mul cbits putinterval
  502.  
  503. %  Compute the font entry, converting the bitmap to an outline.
  504.      bits 0 raster fbbh mul getinterval    % the bitmap image
  505.      bbw   fbbh                % bitmap width & height
  506.      swx   swy                % width x & y
  507.      bbox neg   bboy neg            % origin x & y
  508.          % Account for lenIV when converting the outline.
  509.      outline  lenIV  outline length lenIV sub  getinterval
  510.      type1imagepath
  511.      length lenIV add
  512.      outline exch 0 exch getinterval
  513.  
  514. % Check for a fixed width font.
  515.      isfixedwidth
  516.       { fixedwidth null eq
  517.          { /fixedwidth swx def }
  518.          { fixedwidth swx ne { /isfixedwidth false def } if }
  519.         ifelse
  520.       } if
  521.  
  522. % Finish up the character.
  523.      copystring
  524.      charname exch charstrings 3 1 roll put
  525.        } forall
  526.  
  527. %  Add CharStrings entries for aliases.
  528.       aliases
  529.        { charstrings 2 index known not charstrings 2 index known and
  530.           { charstrings exch get charstrings 3 1 roll put
  531.       }
  532.       { pop pop
  533.       }
  534.      ifelse
  535.        }
  536.       forall
  537.  
  538. %  If this is not a fixed-width font, synthesize missing characters
  539. %  out of available ones.
  540.       isfixedwidth not
  541.        { false composites
  542.       { 1 index charstrings exch known not
  543.         1 index { decoding exch known and } forall
  544.          { ( /) print 1 index bits cvs print
  545.            /combine exch def
  546.            0 1 combine length 1 sub
  547.         { dup combine exch get decoding exch get
  548.           bits 3 1 roll put
  549.         } for
  550.            bits 0 combine length getinterval copystring
  551.            [ exch /compose_proc load aload pop ] cvx
  552.            charstrings 3 1 roll put
  553.            pop true
  554.          }
  555.          { pop pop }
  556.         ifelse
  557.       }
  558.      forall flush
  559.       { Private /composematrix matrix put
  560.         Private /compose /compose load put
  561.       }
  562.      if
  563.        }
  564.       if
  565.  
  566. %  Synthesize accented characters with seac if needed and possible.
  567.       accentedchars
  568.        { aload pop /accent exch def /base exch def
  569.          buffer cvs /accented exch def
  570.      charstrings accented known not
  571.      charstrings base known and
  572.      charstrings accent known and
  573.      StandardDecoding base known and
  574.      StandardDecoding accent known and
  575.      encoding StandardDecoding base get get base eq and
  576.      encoding StandardDecoding accent get get accent eq and
  577.       { ( /) print accented print
  578.         charstrings base get findsbw 0 exch getinterval
  579.         /acstring exch def        % start with sbw of base
  580.         charstrings accent get parsesbw
  581.         4 { pop } repeat        % just leave sbx
  582.         acstring exch concatnum
  583.         0 concatnum 0 concatnum        % adx ady
  584.         decoding base get concatnum        % bchar
  585.         decoding accent get concatnum    % achar
  586.         s_seac concatstrings
  587.         charstrings exch accented copystring exch put
  588.       } if
  589.        } forall
  590.  
  591. %  Make a CharStrings entry for .notdef.
  592.       outline lenIV <8b8b0d0e> putinterval    % 0 0 hsbw endchar
  593.       charstrings /.notdef outline 0 lenIV 4 add getinterval copystring put
  594.  
  595. %  Encrypt the CharStrings and Subrs (in place).
  596.       charstrings
  597.        {    % Be careful not to encrypt aliased characters twice,
  598.         % since they share their CharString.
  599.      aliases 2 index known
  600.       { charstrings aliases 3 index get .knownget
  601.          { 1 index ne }
  602.          { true }
  603.         ifelse
  604.       }
  605.       { true
  606.       }
  607.      ifelse
  608.      1 index type /stringtype eq and
  609.           { 4330 exch dup .type1encrypt exch pop
  610.         readonly charstrings 3 1 roll put
  611.       }
  612.       { pop pop
  613.       }
  614.      ifelse
  615.        }
  616.       forall
  617.       0 1 subrcount 1 sub
  618.        { dup subrs exch get
  619.      4330 exch dup .type1encrypt exch pop
  620.      subrs 3 1 roll put
  621.        }
  622.       for
  623.  
  624. %  Make most of the remaining entries in the font dictionaries.
  625.  
  626. % The Type 1 font machinery really only works with a 1000 unit
  627. % character coordinate system.  Set this up here, by computing the factor
  628. % to make the X entry in the FontMatrix come out at exactly 0.001.
  629.       /fontscale 1000 fbbh div yres mul xres div def
  630.       Font /FontBBox
  631.        [ fbbxo fontscale mul
  632.      fbbyo fontscale mul
  633.      fbbxe fontscale mul
  634.      fbbye fontscale mul
  635.        ] cvx readonly put
  636.       Font /CharStrings charstrings readonly put
  637.       FontInfo /FullName known not
  638.        { % Some programs insist on FullName being present.
  639.          FontInfo /FullName FontName dup length string cvs put
  640.        }
  641.       if
  642.       FontInfo /isFixedPitch isfixedwidth put
  643.       subrcount 0 gt
  644.        { Private /Subrs subrs 0 subrcount getinterval readonly put
  645.        } if
  646.  
  647. %  Determine the italic angle and underline position
  648. %  by actually installing the font.
  649.       save
  650.       /_temp_ Font definefont setfont
  651.       [1000 0 0 1000 0 0] setmatrix        % mitigate rounding problems
  652. % The italic angle is the multiple of -5 degrees
  653. % that minimizes the width of the 'I'.
  654.       0 9999 0 5 85
  655.        { dup rotate
  656.          newpath 0 0 moveto (I) false charpath
  657.      dup neg rotate
  658.          pathbbox pop exch pop exch sub
  659.      dup 3 index lt { 4 -2 roll } if
  660.      pop pop
  661.        }
  662.       for pop
  663. % The underline position is halfway between the bottom of the 'A'
  664. % and the bottom of the FontBBox.
  665.       newpath 0 0 moveto (A) false charpath
  666.       FontMatrix concat
  667.       pathbbox pop pop exch pop
  668. %  Put the values in FontInfo.
  669.       3 -1 roll
  670.       restore
  671.       Font /FontBBox get 1 get add 2 div cvi
  672.       dup FontInfo /UnderlinePosition 3 -1 roll put
  673.       2 div abs FontInfo /UnderlineThickness 3 -1 roll put
  674.       FontInfo /ItalicAngle 3 -1 roll put
  675.  
  676. %  Clean up and finish.
  677.       grestore
  678.       bdfile closefile
  679.       Font currentdict end end begin        % remove font from dict stack
  680.       (\n) print flush
  681.  
  682.     } bind def
  683.  
  684. % ------ Reader for AFM files ------ %
  685.  
  686. % Dictionary for looking up character keywords
  687.    /cmdict 6 dict dup begin
  688.       /C { /c iarg def } def
  689.       /N { /n warg copystring def } def
  690.       /WX { /w narg def } def
  691.       /W0X /WX load def
  692.       /W /WX load def
  693.       /W0 /WX load def
  694.    end def
  695.  
  696.    /readAFM        % fontdict afmfilename readAFM -> fontdict
  697.     { (r) file /bdfile exch def
  698.       /Font exch def
  699.       /commentword (Comment) def
  700.  
  701. %  Check for the StartFontMetrics.
  702.       (StartFontMetrics) getline
  703.       args cvr 2.0 lt { (Not version 2.0 or greater\n) print stop } if
  704.  
  705. %  Look for StartCharMetrics, then parse the character metrics.
  706. %  The only information we care about is the X width.
  707.       /metrics 0 dict def
  708.        { nextline
  709.          keyword (EndFontMetrics) eq { exit } if
  710.      keyword (StartCharMetrics) eq
  711.       { iarg dup dict /metrics exch def
  712.          { /c -1 def /n null def /w null def
  713.            nextline buffer
  714.         { token not { exit } if
  715.           dup cmdict exch known
  716.            { exch /args exch def   cmdict exch get exec   args }
  717.            { pop }
  718.           ifelse
  719.         } loop
  720.            c 0 ge n null ne or w null ne and
  721.         { n null eq { /n Font /Encoding get c get def } if
  722.           metrics n w put
  723.         }
  724.            if
  725.          }
  726.         repeat
  727.         (EndCharMetrics) getline
  728.       } if
  729.        } loop
  730.  
  731. %  Insert the metrics in the font.
  732.        metrics length 0 ne
  733.     { Font /Metrics metrics readonly put
  734.     } if
  735.       Font
  736.     } bind def
  737.  
  738. end        % envBDF
  739.  
  740. % Enter the main program in the current dictionary.
  741. /bdfafmtops        % infilename afmfilename* outfilename fontname
  742.             %   encodingname uniqueID xuid
  743.  { envBDF begin
  744.      7 -2 roll exch 7 2 roll    % afm* in out fontname encodingname uniqueID xuid
  745.      readBDF        % afm* font
  746.      exch { readAFM } forall
  747.      save exch
  748.      dup /FontName get exch definefont
  749.      setfont
  750.      psfile writefont
  751.      restore
  752.      psfile closefile
  753.    end
  754.  } bind def
  755.  
  756. % If the program was invoked from the command line, run it now.
  757. [ shellarguments
  758.  { counttomark 4 ge
  759.     { dup 0 get
  760.       dup 48 ge exch 57 le and        % last arg starts with a digit?
  761.        { /StandardEncoding }        % no encodingname
  762.        { cvn }                % have encodingname
  763.       ifelse
  764.       exch (.) search            % next-to-last arg has . in it?
  765.        { mark 4 1 roll            % have xuid
  766.           { cvi exch pop exch (.) search not { exit } if }
  767.      loop cvi ]
  768.      3 -1 roll cvi exch
  769.        }
  770.        { cvi null            % no xuid
  771.        }
  772.       ifelse
  773.       counttomark 5 roll
  774.       counttomark 6 sub array astore
  775.       7 -2 roll cvn 7 -3 roll        % make sure fontname is a name
  776.       bdfafmtops
  777.     }
  778.     { cleartomark
  779.       (Usage:\n  bdftops xx.bdf [yy1.afm ...] zz.gsf fontname uniqueID [xuid] [encodingname]\n) print flush
  780.       mark
  781.     }
  782.    ifelse
  783.  }
  784. if pop
  785.